// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/trace_event/trace_event_memory_overhead.h"

#include <algorithm>

#include "base/bits.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/values.h"

namespace base {
namespace trace_event {

    TraceEventMemoryOverhead::TraceEventMemoryOverhead()
    {
    }

    TraceEventMemoryOverhead::~TraceEventMemoryOverhead()
    {
    }

    void TraceEventMemoryOverhead::AddOrCreateInternal(
        const char* object_type,
        size_t count,
        size_t allocated_size_in_bytes,
        size_t resident_size_in_bytes)
    {
        auto it = allocated_objects_.find(object_type);
        if (it == allocated_objects_.end()) {
            allocated_objects_.insert(std::make_pair(
                object_type,
                ObjectCountAndSize(
                    { count, allocated_size_in_bytes, resident_size_in_bytes })));
            return;
        }
        it->second.count += count;
        it->second.allocated_size_in_bytes += allocated_size_in_bytes;
        it->second.resident_size_in_bytes += resident_size_in_bytes;
    }

    void TraceEventMemoryOverhead::Add(const char* object_type,
        size_t allocated_size_in_bytes)
    {
        Add(object_type, allocated_size_in_bytes, allocated_size_in_bytes);
    }

    void TraceEventMemoryOverhead::Add(const char* object_type,
        size_t allocated_size_in_bytes,
        size_t resident_size_in_bytes)
    {
        AddOrCreateInternal(object_type, 1, allocated_size_in_bytes,
            resident_size_in_bytes);
    }

    void TraceEventMemoryOverhead::AddString(const std::string& str)
    {
        // The number below are empirical and mainly based on profiling of real-world
        // std::string implementations:
        //  - even short string end up malloc()-inc at least 32 bytes.
        //  - longer strings seem to malloc() multiples of 16 bytes.
        const size_t capacity = bits::Align(str.capacity(), 16);
        Add("std::string", sizeof(std::string) + std::max<size_t>(capacity, 32u));
    }

    void TraceEventMemoryOverhead::AddRefCountedString(
        const RefCountedString& str)
    {
        Add("RefCountedString", sizeof(RefCountedString));
        AddString(str.data());
    }

    void TraceEventMemoryOverhead::AddValue(const Value& value)
    {
        switch (value.GetType()) {
        case Value::TYPE_NULL:
        case Value::TYPE_BOOLEAN:
        case Value::TYPE_INTEGER:
        case Value::TYPE_DOUBLE:
            Add("FundamentalValue", sizeof(Value));
            break;

        case Value::TYPE_STRING: {
            const StringValue* string_value = nullptr;
            value.GetAsString(&string_value);
            Add("StringValue", sizeof(StringValue));
            AddString(string_value->GetString());
        } break;

        case Value::TYPE_BINARY: {
            const BinaryValue* binary_value = nullptr;
            value.GetAsBinary(&binary_value);
            Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize());
        } break;

        case Value::TYPE_DICTIONARY: {
            const DictionaryValue* dictionary_value = nullptr;
            value.GetAsDictionary(&dictionary_value);
            Add("DictionaryValue", sizeof(DictionaryValue));
            for (DictionaryValue::Iterator it(*dictionary_value); !it.IsAtEnd();
                 it.Advance()) {
                AddString(it.key());
                AddValue(it.value());
            }
        } break;

        case Value::TYPE_LIST: {
            const ListValue* list_value = nullptr;
            value.GetAsList(&list_value);
            Add("ListValue", sizeof(ListValue));
            for (const auto& v : *list_value)
                AddValue(*v);
        } break;

        default:
            NOTREACHED();
        }
    }

    void TraceEventMemoryOverhead::AddSelf()
    {
        size_t estimated_size = sizeof(*this);
        // If the SmallMap did overflow its static capacity, its elements will be
        // allocated on the heap and have to be accounted separately.
        if (allocated_objects_.UsingFullMap())
            estimated_size += sizeof(map_type::value_type) * allocated_objects_.size();
        Add("TraceEventMemoryOverhead", estimated_size);
    }

    size_t TraceEventMemoryOverhead::GetCount(const char* object_type) const
    {
        const auto& it = allocated_objects_.find(object_type);
        if (it == allocated_objects_.end())
            return 0u;
        return it->second.count;
    }

    void TraceEventMemoryOverhead::Update(const TraceEventMemoryOverhead& other)
    {
        for (const auto& it : other.allocated_objects_) {
            AddOrCreateInternal(it.first, it.second.count,
                it.second.allocated_size_in_bytes,
                it.second.resident_size_in_bytes);
        }
    }

    void TraceEventMemoryOverhead::DumpInto(const char* base_name,
        ProcessMemoryDump* pmd) const
    {
        for (const auto& it : allocated_objects_) {
            std::string dump_name = StringPrintf("%s/%s", base_name, it.first);
            MemoryAllocatorDump* mad = pmd->CreateAllocatorDump(dump_name);
            mad->AddScalar(MemoryAllocatorDump::kNameSize,
                MemoryAllocatorDump::kUnitsBytes,
                it.second.allocated_size_in_bytes);
            mad->AddScalar("resident_size", MemoryAllocatorDump::kUnitsBytes,
                it.second.resident_size_in_bytes);
            mad->AddScalar(MemoryAllocatorDump::kNameObjectCount,
                MemoryAllocatorDump::kUnitsObjects, it.second.count);
        }
    }

} // namespace trace_event
} // namespace base
